package com.docchatlink.iflytek;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.docchatlink.constant.MethodEnum;
import com.docchatlink.dto.chat.ChatMessage;
import com.docchatlink.dto.chat.ChatRequest;
import com.docchatlink.iflytek.config.IflytekConfig;
import com.docchatlink.util.ApiAuthUtil;
import com.docchatlink.util.BeanUtils;
import com.docchatlink.util.RedisUtil;
import com.docchatlink.vo.AnswerChatVO;
import com.docchatlink.vo.QuestionChatVO;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
 * @author dayon
 */
@ServerEndpoint("/chat/{sid}")
@Slf4j
@Service
public class WebSocketServer {

    private static final Map<String, Session> ONLINE_SESSION_CLIENT_MAP = new ConcurrentHashMap<>();

    private static final Map<String, Integer> REMOVE_TEMP = new ConcurrentHashMap<>();

    private static final Map<String, MyWebSocket> ONLINE_SESSION_SERVER_MAP = new ConcurrentHashMap<>();

    private static final Map<String, String> SID_XFDOCSID = new ConcurrentHashMap<>();

    private static final Map<String, List<ChatMessage>> SID_CHAT_MESSAGE = new ConcurrentHashMap<>();
    //会话唯一标识
    private String sid;

    private Session session;

    private final Integer sleepTime = 30;

    @OnOpen
    public void onOpen(@PathParam("sid") String sid, Session session) {
        log.info("前端与本地连接建立中 ==》 session_id = {},sid={}", session.getId(), sid);
        if (REMOVE_TEMP.containsKey(sid)) {
            REMOVE_TEMP.remove(sid);
        }
        ONLINE_SESSION_CLIENT_MAP.put(sid, session);

        this.sid = sid;
        this.session = session;
        log.info("连接建立成功， ==> 开始监听新连接：session_id = {}， sid = {},。", session.getId(), sid);
    }


    @OnClose
    public void onClose(@PathParam("sid") String sid, Session session) {
        // 从 Map中移除
        if (!ONLINE_SESSION_CLIENT_MAP.containsKey(sid)) {
            return;
        }
        if (ONLINE_SESSION_SERVER_MAP.containsKey(sid)) {
            ONLINE_SESSION_SERVER_MAP.remove(sid);
        }
        ONLINE_SESSION_CLIENT_MAP.remove(sid);

        REMOVE_TEMP.put(sid, sleepTime);
        //在线数减1
        log.info("连接关闭成功 ==> 关闭该连接信息：session_id = {}， sid = {},。", session.getId(), sid);
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("从前端接收到信息 ==> 接收信息 = {},。", message);
        QuestionChatVO questionChatVO = JSONObject.parseObject(message, QuestionChatVO.class);

        List<ChatMessage> hisMessages = new ArrayList<>();
        //获取历史会话
        if (!CollectionUtils.isEmpty(SID_CHAT_MESSAGE.get(sid))) {
            hisMessages = SID_CHAT_MESSAGE.get(sid);
        }
        ChatMessage chatMessage = new ChatMessage();
        chatMessage.setRole("user");
        chatMessage.setContent(questionChatVO.getContent());
        hisMessages.add(chatMessage);
        // 请求内容
        ChatRequest chatRequest = ChatRequest.builder()
                .fileIds(questionChatVO.getFileIds())
                .topN(3)
                .messages(hisMessages)
                .build();

        // 构造url鉴权
        IflytekConfig config = BeanUtils.getBean(IflytekConfig.class);
        long ts = System.currentTimeMillis() / 1000;
        String signature = ApiAuthUtil.getSignature(config.getAppId(), config.getApiSecret(), ts);
        String requestUrl = config.getChatUrl() + "?" + "appId=" + config.getAppId() + "&timestamp=" + ts + "&signature=" + signature;
        // ws
        Request wsRequest = (new Request.Builder()).url(requestUrl).build();
        OkHttpClient okHttpClient = new OkHttpClient().newBuilder().build();

        MyWebSocket myWebSocket = new MyWebSocket().create(wsRequest, okHttpClient, sid, this);
        log.info("接收到的消息：{}", JSONUtil.toJsonStr(chatRequest));
        myWebSocket.sendMessage(JSONUtil.toJsonStr(chatRequest));

        ONLINE_SESSION_SERVER_MAP.put(sid, myWebSocket);
        ONLINE_SESSION_CLIENT_MAP.put(sid, session);
    }


    /**
     * ·
     * 发生错误调用的方法
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("WebSocket发生错误，错误信息为：" + error.getMessage());
        error.printStackTrace();
    }

    /**
     * 群发消息
     *
     * @param message 消息
     */
    private void sendToAll(String message) {
        // 遍历在线map集合
        ONLINE_SESSION_CLIENT_MAP.forEach((onlineSid, toSession) -> {
            // 排除掉自己
            if (!sid.equalsIgnoreCase(onlineSid)) {
                log.info("服务端给客户端群发消息 ==> sid = {}, toSid = {}, message = {}", sid, onlineSid, message);
                toSession.getAsyncRemote().sendText(message);
            }
        });
    }

    /**
     * 指定发送消息
     *
     * @param toSid
     * @param message
     */
    public void sendToOne(String toSid, String message) {
        // 通过sid查询map中是否存在
        Session toSession = ONLINE_SESSION_CLIENT_MAP.get(toSid);
        if (toSession == null) {
            log.error("服务端给客户端发送消息 ==> toSid = {} 不存在, message = {}", toSid, message);
//            session.getAsyncRemote().sendText(JSONObject.toJSONString(new ChatVO().setMethodEnum(MethodEnum.ERROR).setParams("请求的客户端不存在")));
            return;
        }
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            log.error("线程休眠异常", e);
        }
        // 处理message
        AnswerChatVO chatVO = JSONObject.parseObject(message, AnswerChatVO.class);
        if (chatVO.getCode().equals(MethodEnum.RESP_ALL.getCode())) {
            ChatMessage respChatMessage = new ChatMessage();
            respChatMessage.setRole("assistant");
            respChatMessage.setContent((String) chatVO.getContent());
            addHistory(sid, respChatMessage);
        } else {
            toSession.getAsyncRemote().sendText(message);
        }
    }

    public void removeSid() {
        for (String sid : REMOVE_TEMP.keySet()) {
            if (REMOVE_TEMP.get(sid).intValue() == 0) {
                log.info("服务端移除客户端 ==> sid = {}", sid);
                RedisUtil bean = BeanUtils.getBean(RedisUtil.class);
                bean.del(sid);
                bean.del(sid + "HISTORY");
                REMOVE_TEMP.remove(sid);
                return;
            }
            REMOVE_TEMP.put(sid, sleepTime);
        }
    }

    /**
     * 判断sid是否超时
     */
    public boolean sidAlive(String sid) {
        return ONLINE_SESSION_CLIENT_MAP.containsKey(sid) || REMOVE_TEMP.containsKey(sid);
    }

    public void removeServer(String sid) {
        ONLINE_SESSION_SERVER_MAP.remove(sid);
    }

    /**
     * 历史会话保存到缓存
     *
     * @param sid         会话唯一sid
     * @param chatMessage 历史信息
     */
    public void addHistory(String sid, ChatMessage chatMessage) {
        if (!CollectionUtils.isEmpty(SID_CHAT_MESSAGE.get(sid))) {
            List<ChatMessage> list = SID_CHAT_MESSAGE.get(sid);
            list.add(chatMessage);
            SID_CHAT_MESSAGE.put(sid, list);
        } else {
            List<ChatMessage> list = new ArrayList<>();
            list.add(chatMessage);
            SID_CHAT_MESSAGE.put(sid, list);
        }
    }

}
